/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 point_2d.h

	$Header: /heroes4/point_2d.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( POINT_2D_H_INCLUDED )
#define POINT_2D_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <algorithm>

// --------------------------------------------------------------------------
// t_point_2d class template - Represents a two dimensional point in screen
// coordinates.
// --------------------------------------------------------------------------

template < typename t_coord_arg >
struct t_point_2d
{
public:
	// Types
	typedef t_coord_arg t_coord;

	// Data members
	t_coord	x;
	t_coord	y;

	// Constructors
	t_point_2d();
	t_point_2d( t_coord const & x_arg, t_coord const & y_arg );

	template < typename t_other_coord >
	t_point_2d( t_point_2d< t_other_coord > const & other )
		:	x( other.x ),
			y( other.y )
	{
	}

	t_point_2d( t_point_2d const & other );

	// Operators
	t_point_2d & operator+=( t_point_2d const & right );
	t_point_2d & operator-=( t_point_2d const & right );
	t_point_2d & operator*=( t_coord const & right );
	t_point_2d & operator/=( t_coord const & right );
	t_point_2d & operator%=( t_coord const & right );

	// Friend functions - defined inline to provide automatic intantiation
	friend bool operator==( t_point_2d const & left, t_point_2d const & right )
	{
		return left.x == right.x && left.y == right.y;
	}

	friend bool operator!=( t_point_2d const & left, t_point_2d const & right )
	{
		return !( left == right );
	}

	friend t_point_2d operator+( t_point_2d const & point )
	{
		return point;
	}

	friend t_point_2d operator-( t_point_2d const & point )
	{
		return t_point_2d( -point.x, -point.y );
	}

	friend t_point_2d operator+( t_point_2d const & left, t_point_2d const & right )
	{
		t_point_2d result( left );
		return result += right;
	}

	friend t_point_2d operator-( t_point_2d const & left, t_point_2d const & right )
	{
		t_point_2d result( left );
		return result -= right;
	}

	friend t_point_2d operator*( t_point_2d const & left, t_coord const & right )
	{
		t_point_2d result( left );
		return result *= right;
	}

	friend t_point_2d operator*( t_coord const & left, t_point_2d const & right )
	{
		return right * left;
	}

	friend t_point_2d operator/( t_point_2d const & left, t_coord const & right )
	{
		t_point_2d result( left );
		return result /= right;
	}

	friend t_point_2d operator%( t_point_2d const & left, t_coord const & right )
	{
		t_point_2d result( left );
		return result %= right;
	}
};

// --------------------------------------------------------------------------
// t_point_2d inline members
// --------------------------------------------------------------------------

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg >::t_point_2d()
	:	x( 0 ),
		y( 0 )
{
}

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg >::t_point_2d( t_coord const & x_arg, t_coord const & y_arg )
	:	x( x_arg ),
		y( y_arg )
{
}

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg >::t_point_2d( t_point_2d const & other )
	:	x( other.x ),
		y( other.y )
{
}

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg > & t_point_2d< t_coord_arg >::operator+=( t_point_2d const & arg )
{
	x += arg.x;
	y += arg.y;
	return *this;
}

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg > & t_point_2d< t_coord_arg >::operator-=( t_point_2d const & arg )
{
	x -= arg.x;
	y -= arg.y;
	return *this;
}

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg > & t_point_2d< t_coord_arg >::operator*=( t_coord const & arg )
{
	x *= arg;
	y *= arg;
	return *this;
}

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg > & t_point_2d< t_coord_arg >::operator/=( t_coord const & arg )
{
	x /= arg;
	y /= arg;
	return *this;
}

template < typename t_coord_arg >
inline t_point_2d< t_coord_arg > & t_point_2d< t_coord_arg >::operator%=( t_coord const & arg )
{
	x %= arg;
	y %= arg;
	return *this;
}

// --------------------------------------------------------------------------
// t_rect_2d class template - Represents a two dimensional rectangle in
// screen coordinates.
// --------------------------------------------------------------------------

template < typename t_coord_arg >
struct t_rect_2d
{
public:
	// Types
	typedef t_coord_arg				t_coord;
	typedef t_point_2d< t_coord >	t_point;

	// Data members
	t_coord	left;
	t_coord	top;
	t_coord	right;
	t_coord	bottom;

	// Constructors
	t_rect_2d();
	t_rect_2d(
		t_coord const & left_arg,
		t_coord const & top_arg,
		t_coord const & right_arg,
		t_coord const & bottom_arg );
	t_rect_2d( t_point const & top_left, t_point const & size );

	template < typename t_other_coord >
	t_rect_2d( t_rect_2d< t_other_coord > const & other )
		:	left( other.left ),
			top( other.top ),
			right( other.right ),
			bottom( other.bottom )
	{
	}

	t_rect_2d( t_rect_2d const & other );

	// Member functions
	t_coord	height() const;
	t_coord	width() const;

	t_point	top_left() const;
	t_point	top_right() const;
	t_point	bottom_left() const;
	t_point	bottom_right() const;

	t_point	size() const;

	bool	contains( t_rect_2d const & arg ) const; // true if rectangle entirely
                                                               // contains arg.
	// Operators
	t_rect_2d & operator+=( t_point const & point );
	t_rect_2d & operator-=( t_point const & point );
	t_rect_2d & operator*=( t_coord const & arg );
	t_rect_2d & operator/=( t_coord const & arg );

	// Friend functions - defined inline to provide automatic intantiation
	friend bool operator==( t_rect_2d const & left, t_rect_2d const & right )
	{
		return		left.left == right.left
				&&	left.top == right.top
				&&	left.right == right.right
				&&	left.bottom == right.bottom;
	}

	friend bool operator!=( t_rect_2d const & left, t_rect_2d const & right )
	{
		return !( left == right );
	}

	friend t_rect_2d operator+( t_rect_2d const & left, t_point const & right )
	{
		t_rect_2d result( left );
		return result += right;
	}

	friend t_rect_2d operator+( t_point const & left, t_rect_2d const & right )
	{
		return right + left;
	}

	friend t_rect_2d operator-( t_rect_2d const & left, t_point const & right )
	{
		t_rect_2d result( left );
		return result -= right;
	}

	friend t_rect_2d operator*( t_rect_2d const & left, t_coord const & right )
	{
		t_rect_2d result( left );
		return result *= right;
	}

	friend t_rect_2d operator*( t_coord const & left, t_rect_2d const & right )
	{
		return right * left;
	}

	friend t_rect_2d operator/( t_rect_2d const & left, t_coord const & right )
	{
		t_rect_2d result( left );
		return result /= right;
	}
};

// --------------------------------------------------------------------------
// Inline members for t_rect_2d
// --------------------------------------------------------------------------

template < typename t_coord_arg >
inline t_rect_2d< t_coord_arg >::t_rect_2d()
	:	left( 0 ),
		top( 0 ),
		right( 0 ),
		bottom( 0 )
{
}

template < typename t_coord_arg >
inline t_rect_2d< t_coord_arg >::t_rect_2d(
	t_coord const & left_arg,
	t_coord const & top_arg,
	t_coord const & right_arg,
	t_coord const & bottom_arg )
	:	left( left_arg ),
		top( top_arg ),
		right( right_arg ),
		bottom( bottom_arg )
{
}

template < typename t_coord_arg >
inline t_rect_2d< t_coord_arg >::t_rect_2d(
	t_point const & top_left,
	t_point const & size )
	:	left( top_left.x ),
		top( top_left.y ),
		right( top_left.x + size.x ),
		bottom( top_left.y + size.y )
{
}

template < typename t_coord_arg >
inline t_rect_2d< t_coord_arg >::t_rect_2d( t_rect_2d const & other )
	:	left( other.left ),
		top( other.top ),
		right( other.right ),
		bottom( other.bottom )
{
}

template < typename t_coord_arg >
inline typename t_rect_2d< t_coord_arg >::t_coord t_rect_2d< t_coord_arg >::height() const
{
	return bottom - top;
}

template < typename t_coord_arg >
inline typename t_rect_2d< t_coord_arg >::t_coord t_rect_2d< t_coord_arg >::width() const
{
	return right - left;
}

template < typename t_coord_arg >
inline typename t_rect_2d< t_coord_arg >::t_point t_rect_2d< t_coord_arg >::top_left() const
{
	return t_point( left, top );
}

template < typename t_coord_arg >
inline typename t_rect_2d< t_coord_arg >::t_point t_rect_2d< t_coord_arg >::top_right() const
{
	return t_point( right, top );
}

template < typename t_coord_arg >
inline typename t_rect_2d< t_coord_arg >::t_point t_rect_2d< t_coord_arg >::bottom_left() const
{
	return t_point( left, bottom );
}

template < typename t_coord_arg >
inline typename t_rect_2d< t_coord_arg >::t_point t_rect_2d< t_coord_arg >::bottom_right() const
{
	return t_point( right, bottom );
}

template < typename t_coord_arg >
inline typename t_rect_2d< t_coord_arg >::t_point t_rect_2d< t_coord_arg >::size() const
{
	return t_point( width(), height() );
}

template < typename t_coord_arg >
inline bool t_rect_2d< t_coord_arg >::contains( t_rect_2d const & arg ) const
{
	return		left <= arg.left
			&&	right >= arg.right
			&&	top <= arg.top
			&&	bottom >= arg.bottom;
}

template < typename t_coord_arg >
t_rect_2d< t_coord_arg > & t_rect_2d< t_coord_arg >::operator+=( t_point const & point )
{
	left += point.x;
	top += point.y;
	right += point.x;
	bottom += point.y;
	return *this;
}

template < typename t_coord_arg >
t_rect_2d< t_coord_arg > & t_rect_2d< t_coord_arg >::operator-=( t_point const & point )
{
	left -= point.x;
	top -= point.y;
	right -= point.x;
	bottom -= point.y;
	return *this;
}

template < typename t_coord_arg >
t_rect_2d< t_coord_arg > & t_rect_2d< t_coord_arg >::operator*=( t_coord const & arg )
{
	left *= arg;
	top *= arg;
	right *= arg;
	bottom *= arg;
	return *this;
}

template < typename t_coord_arg >
t_rect_2d< t_coord_arg > & t_rect_2d< t_coord_arg >::operator/=( t_coord const & arg )
{
	left /= arg;
	top /= arg;
	right /= arg;
	bottom /= arg;
	return *this;
}

// --------------------------------------------------------------------------
// Inline free functions for t_rect_2d
// --------------------------------------------------------------------------

template < typename t_coord >
inline bool is_point_in_rect( t_point_2d< t_coord > const & point, t_rect_2d< t_coord > const & rect )
{
	return		point.x >= rect.left
			&&	point.x < rect.right
			&&	point.y >= rect.top
			&&	point.y < rect.bottom;
}

template < typename t_coord >
inline bool is_normalized( t_rect_2d< t_coord > const & rect )
{
	return rect.left <= rect.right && rect.top <= rect.bottom;
}

template < typename t_coord >
inline void normalize( t_rect_2d< t_coord > & rect )
{
	if ( rect.left > rect.right )
		std::swap( rect.left, rect.right );

	if ( rect.top > rect.bottom )
		std::swap( rect.top, rect.bottom );
}

template < typename t_coord >
inline bool intersect( t_rect_2d< t_coord > const & first, t_rect_2d< t_coord > const & second )
{
	return		first.left < second.right
			&&	second.left < first.right
			&&	first.top < second.bottom
			&&	second.top < first.bottom;
}

template < typename t_coord >
inline void assign_intersection( t_rect_2d< t_coord > & first, t_rect_2d< t_coord > const & second )
{
	if ( second.left > first.left )
		first.left = second.left;
	if ( second.top > first.top )
		first.top = second.top;
	if ( second.right < first.right )
		first.right = second.right;
	if ( second.bottom < first.bottom )
		first.bottom = second.bottom;
}

template < typename t_coord >
inline t_rect_2d< t_coord > intersection( t_rect_2d< t_coord > const & first, t_rect_2d< t_coord > const & second )
{
	t_rect_2d< t_coord > result( first );
	assign_intersection( result, second );
	return result;
}

template < typename t_coord >
inline void assign_extent( t_rect_2d< t_coord > & first, t_rect_2d< t_coord > const & second )
{
	if ( second.left < first.left )
		first.left = second.left;
	if ( second.top < first.top )
		first.top = second.top;
	if ( second.right > first.right )
		first.right = second.right;
	if ( second.bottom > first.bottom )
		first.bottom = second.bottom;
}

template < typename t_coord >
inline t_rect_2d< t_coord > get_extent( t_rect_2d< t_coord > const & first, t_rect_2d< t_coord > const & second )
{
	t_rect_2d< t_coord > result( first );
	assign_extent( result, second );
	return result;
}

#endif // !defined( POINT_2D_H_INCLUDED )
